﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
using SanctusSoftware.Music.Lyrics;

namespace SanctusSoftware.Music.Lyrics.Extraction
{
    
    public abstract class WebSongLyricsExtractor : ISongLyricsExtractor
    {

        #region Public Properties

        private string url;

        //public string Url
        //{
        //    get { return url; }
        //    set { url = value; }
        //}

        //private string title;

        //public string Title
        //{
        //    get { return title; }
        //}

        //private string[] verses;

        //public string[] Verses
        //{
        //    get { return verses; }
        //}


        #endregion

        #region Public Methods

        public SongLyrics ExtractSongFromUrl(string url)
        {

            WebClient webClient = new WebClient();

            byte[] reqHTML;
            reqHTML = webClient.DownloadData(url);
            UTF8Encoding objUTF8 = new UTF8Encoding();

            string pageText = objUTF8.GetString(reqHTML);

            SongLyrics s = ExtractSongFromHtml(pageText);

            s.Source += " (" + url + ")";
            return s;


        }

        public SongLyrics ExtractSongFromHtml(string html)
        {

            SongLyrics s = new SongLyrics();


            // Process Title
            s.Title = GetSongTitle(html);

            // Process Source
            s.Source = GetSource(html);

            

            // Process Lyrics
            string rawLyrics = GetRawLyrics(html);

            if (string.IsNullOrEmpty(rawLyrics))
            {
                throw new Exception("No song lyrics found");
            }

            rawLyrics = ReplaceHtmlCodesWithCharacters(rawLyrics);

            rawLyrics = ReplaceLFWithCRLF(rawLyrics);

            rawLyrics = TrimExtraLines(rawLyrics);

            string[] verses = GetVerses(rawLyrics);

            foreach (string verse in verses)
            {
                SongLyricsPart sp = new SongLyricsPart();
                sp.Words = verse;
                s.SongParts.Add(sp);
            }

            return s;

        }

        #endregion

        #region Protected Methods

        protected string GetTextBetweenTags(string pageText, string tagName, bool shortestMatch)
        {
            string nonGreddy = "";
            if (shortestMatch)
            {
                nonGreddy = "?";
            }
            string pattern = "[<]" + tagName + "[>](?<Text>(\\w|\\W)*" + nonGreddy + ")[<]/" + tagName + "[>]";
            return Regex.Match(pageText, pattern).Groups["Text"].Value;
        }


        #endregion

        #region Private Methods

        private string ReplaceHtmlCodesWithCharacters(string text)
        {

            // Convert Unicode Codes into Characters
            foreach (Match m in Regex.Matches(text, @"[&][#](?<Code>[0-9]{1,5});"))
            {

                string originalText = m.Value;

                int code = int.Parse(m.Groups["Code"].Value);

                string replacementText = Convert.ToChar(code).ToString();

                text = text.Replace(originalText, replacementText);


            }

            return text;

        }

        private string TrimExtraLines(string text)
        {
            return text.Trim(' ', (char)13, (char)10);
        }

        private string ReplaceLFWithCRLF(string text)
        {

            char[] chars = text.ToCharArray();

            for (int i = chars.Length - 1; i >= 0; i--)
            {

                

                if (chars[i] == '\n')
                {

                    if (i == chars.Length -1 || chars[i - 1] != '\r')
                    {
                        text = text.Insert(i, "\r");
                    }

                }


            }

            return text;


        }

        #endregion

        #region Abstract Methods

        protected abstract string GetRawLyrics(string pageText);
        protected abstract string GetSongTitle(string pageText);
        
        // TODO: Dictionary with Song Part Title and Text
        protected abstract string[] GetVerses(string rawLyrics);

        protected virtual string GetSource(string html)
        {
            return "";
        }

        #endregion


    }

}
